#ifndef INCLUDED_PCAP_HPP
#define INCLUDED_PCAP_HPP

#include <iostream>
#include <cassert>
#include <stdint.h>
#include <cstdio>

// this is a crude C++ (back)port of...
// easypcap by Tim Pinkawa
// http://www.timpinkawa.net/misc/easypcap.html
// BSD Licensed

typedef uint8_t u_char;
#define PCAP_ERRBUF_SIZE 80
#define DLT_NULL 0
#define DLT_EN10MB 1
#define DLT_IEEE802_11_RADIO 127

namespace {
	uint16_t myget16(FILE* f, bool byteSwap) {
		uint16_t i;
		size_t r = fread(&i, 1, sizeof(uint16_t), f);
		assert(r == sizeof(uint16_t));
		return i;
	}
	uint32_t myget32(FILE* f, bool byteSwap) {
		uint32_t i;
		size_t r = fread(&i, 1, sizeof(uint32_t), f);
		assert(r == sizeof(uint32_t));
		return i;
	}
}

typedef struct pcap_pkthdr {
	uint32_t secs;
	uint32_t usecs;
	uint32_t caplen;
	uint32_t len;
};

class pcap_t {
	public:

	static const uint32_t TCPDUMP_MAGIC = 0xa1b2c3d4;
	static const uint32_t TCPDUMP_MAGIC_ENDIAN = 0xd4c3b2a1;

	FILE* f;
	bool byteSwap;

	uint16_t version_major;
	uint16_t version_minor;
	uint32_t snaplen;
	uint32_t thiszone;
	uint32_t sigfigs;
	uint32_t linktype;

	u_char data[65535];

	pcap_t(const char* fname, char errbuf[PCAP_ERRBUF_SIZE]) {
		f = fopen(fname, "rb");
		uint32_t magic = myget32(f, false);
		assert ((magic == TCPDUMP_MAGIC) || (magic == TCPDUMP_MAGIC_ENDIAN));
		byteSwap = (magic == TCPDUMP_MAGIC);
		fclose(f);

		f = fopen(fname, "rb");
		magic = myget32(f, byteSwap);
		assert(magic == TCPDUMP_MAGIC);

		version_major = myget16(f, byteSwap);
		version_minor = myget16(f, byteSwap);

		thiszone = myget32(f, byteSwap);
		sigfigs = myget32(f, false);
		snaplen = myget32(f, byteSwap);
		linktype = myget32(f, byteSwap);
	}

	~pcap_t() {
		fclose(f);
	}

	const u_char* pcap_next(struct pcap_pkthdr* hdr) {
		hdr->secs = myget32(f, byteSwap);
		hdr->usecs = myget32(f, byteSwap);
		hdr->caplen = myget32(f, byteSwap);
		hdr->len = myget32(f, byteSwap);
		assert(hdr->caplen <= 65535);
		size_t r = fread(data, 1, hdr->caplen, f);
		assert(r == hdr->caplen);

		return data;
	}

	uint32_t pcap_datalink() {
		return linktype;
	}

};

inline pcap_t* pcap_open_offline(const char* fname, char errbuf[PCAP_ERRBUF_SIZE]) {
	return new pcap_t(fname, errbuf);
}

inline const u_char* pcap_next(pcap_t* pcap, struct pcap_pkthdr* hdr) {
	return pcap->pcap_next(hdr);
}

inline int pcap_datalink(pcap_t* pcap) {
	return pcap->pcap_datalink();
}

inline void pcap_close(pcap_t* pcap) {
	delete pcap;
}

#endif

